package com.beiding.aggregationcloaddloer;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;

class SingleClassloader extends ClassLoader {

    private String className;

    AggregationClassloader aggregationClassloader;

    //所有的上游类
    private Set<Class> upstream = new HashSet<>();

    //所有的下游类
    private Set<Class> downstream = new HashSet<>();

    private Class clzz;

    SingleClassloader(AggregationClassloader aggregationClassloader, String className) {
        super(null);
        this.aggregationClassloader = aggregationClassloader;
        this.className = className;
    }


    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

        if (name.equals(className)) {
            if (clzz != null) {
                return clzz;
            }
            clzz = super.loadClass(name, resolve);

            for (Class up : upstream) {
                SingleClassloader singleClassloader = (SingleClassloader) up.getClassLoader();
                singleClassloader.downstream.add(clzz);
            }
            return clzz;
        } else {
            Class<?> aClass = aggregationClassloader.controlLoad(name);
            if (aClass != null) {
                ClassLoader classLoader = aClass.getClassLoader();
                if (classLoader != null) {
                    if (classLoader instanceof SingleClassloader) {//记录所有的上游类
                        upstream.add(aClass);
                        if (clzz != null) {//已经完成初始化
                            ((SingleClassloader) classLoader).downstream.add(clzz);
                        }
                    }
                }
            }
            return aClass;
        }
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String path = name.replace('.', '/').concat(".class");
        URL resource = findResource(path);
        if (resource == null) {
            throw new ClassNotFoundException(name);
        }

        try {
            InputStream inputStream = resource.openConnection().getInputStream();
            byte[] buff = new byte[1024];
            int len;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while ((len = inputStream.read(buff)) != -1) {
                byteArrayOutputStream.write(buff, 0, len);
            }
            byte[] bytes = byteArrayOutputStream.toByteArray();
            return defineClass(name, bytes, 0, bytes.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(name);
        }

    }


    @Override
    public URL findResource(String name) {
        return aggregationClassloader.resourceProvider.findResource(name);
    }

    @Override
    public Enumeration<URL> findResources(String name) throws IOException {
        return aggregationClassloader.resourceProvider.findResources(name);
    }

    void destroy(Set<Class> catcher) {

        if (catcher.contains(clzz)) {
            return;
        }
        catcher.add(clzz);

        aggregationClassloader.loadedClasses.remove(className);

        //波及所有下游
        for (Class aClass : downstream) {
            SingleClassloader singleClassloader = (SingleClassloader) aClass.getClassLoader();
            singleClassloader.destroy(catcher);
        }

        for (Class aClass : upstream) {
            SingleClassloader singleClassloader = (SingleClassloader) aClass.getClassLoader();
            singleClassloader.downstream.remove(clzz);
        }

    }

}
